/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.htfyun.hardware;

import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings.Secure;
import android.util.Log;

/**
 * Controller for managing Night display settings.
 * <p/>
 * Night display tints your screen red at night. This makes it easier to look at your screen in
 * dim light and may help you fall asleep more easily.
 */
public final class NightDisplayController {

    private static final String TAG = "NightDisplayController";
    private static final boolean DEBUG = false;

    /**
     * Control whether Night display is currently activated.
     */
    public static final String NIGHT_DISPLAY_ACTIVATED = "night_display_activated";

    /**
     * Control the color temperature of Night Display, represented in Kelvin.
     */
    public static final String NIGHT_DISPLAY_COLOR_TEMPERATURE =
            "night_display_color_temperature";

    private final Context mContext;

    private final ContentObserver mContentObserver;

    private Callback mCallback;

    public NightDisplayController(Context context) {
        mContext = context.getApplicationContext();

        mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
            @Override
            public void onChange(boolean selfChange, Uri uri) {
                super.onChange(selfChange, uri);

                final String setting = uri == null ? null : uri.getLastPathSegment();
                if (setting != null) {
                    onSettingChanged(setting);
                }
            }
        };
    }

    /**
     * Returns {@code true} when Night display is activated (the display is tinted red).
     */
    public boolean isActivated() {
        return Secure.getInt(mContext.getContentResolver(),
                NIGHT_DISPLAY_ACTIVATED, 0) == 1;
    }

    /**
     * Sets whether Night display should be activated. This also sets the last activated time.
     *
     * @param activated {@code true} if Night display should be activated
     * @return {@code true} if the activated value was set successfully
     */
    public boolean setActivated(boolean activated) {
        return Secure.putInt(mContext.getContentResolver(),
                NIGHT_DISPLAY_ACTIVATED, activated ? 1 : 0);
    }

    /**
     * Returns the color temperature (in Kelvin) to tint the display when activated.
     */
    public int getColorTemperature() {
        int colorTemperature = Secure.getInt(mContext.getContentResolver(),
                NIGHT_DISPLAY_COLOR_TEMPERATURE, -1);
        if (colorTemperature == -1) {
            if (DEBUG) {
                Log.d(TAG, "Using default value for setting: "
                    + NIGHT_DISPLAY_COLOR_TEMPERATURE);
            }
            colorTemperature = getDefaultColorTemperature();
        }
        final int minimumTemperature = getMinimumColorTemperature();
        final int maximumTemperature = getMaximumColorTemperature();
        if (colorTemperature < minimumTemperature) {
            colorTemperature = minimumTemperature;
        } else if (colorTemperature > maximumTemperature) {
            colorTemperature = maximumTemperature;
        }

        return colorTemperature;
    }

    /**
     * Sets the current temperature.
     *
     * @param colorTemperature the temperature, in Kelvin.
     * @return {@code true} if new temperature was set successfully.
     */
    public boolean setColorTemperature(int colorTemperature) {
        return Secure.putInt(mContext.getContentResolver(),
            NIGHT_DISPLAY_COLOR_TEMPERATURE, colorTemperature);
    }

    /**
     * Returns the minimum allowed color temperature (in Kelvin) to tint the display when activated.
     */
    public int getMinimumColorTemperature() {
        return 2596;
    }

    /**
     * Returns the maximum allowed color temperature (in Kelvin) to tint the display when activated.
     */
    public int getMaximumColorTemperature() {
        return 4082;
    }

    /**
     * Returns the default color temperature (in Kelvin) to tint the display when activated.
     */
    public int getDefaultColorTemperature() {
        return 2850;
    }

    private void onSettingChanged(String setting) {
        if (DEBUG) {
            Log.d(TAG, "onSettingChanged: " + setting);
        }

        if (mCallback != null) {
            switch (setting) {
                case NIGHT_DISPLAY_ACTIVATED:
                    mCallback.onActivated(isActivated());
                    break;
                case NIGHT_DISPLAY_COLOR_TEMPERATURE:
                    mCallback.onColorTemperatureChanged(getColorTemperature());
                    break;

            }
        }
    }

    /**
     * Register a callback to be invoked whenever the Night display settings are changed.
     */
    public void setListener(Callback callback) {
        final Callback oldCallback = mCallback;
        if (oldCallback != callback) {
            mCallback = callback;

            if (callback == null) {
                // Stop listening for changes now that there IS NOT a listener.
                mContext.getContentResolver().unregisterContentObserver(mContentObserver);
            } else if (oldCallback == null) {
                // Start listening for changes now that there IS a listener.
                final ContentResolver cr = mContext.getContentResolver();
                cr.registerContentObserver(Secure.getUriFor(NIGHT_DISPLAY_ACTIVATED),
                        false /* notifyForDescendants */, mContentObserver);
                cr.registerContentObserver(Secure.getUriFor(NIGHT_DISPLAY_COLOR_TEMPERATURE),
                        false /* notifyForDescendants */, mContentObserver);
            }
        }
    }

    /**
     * Callback invoked whenever the Night display settings are changed.
     */
    public interface Callback {
        /**
         * Callback invoked when the activated state changes.
         *
         * @param activated {@code true} if Night display is activated
         */
        void onActivated(boolean activated);

        /**
         * Callback invoked when the color temperature changes.
         *
         * @param colorTemperature the color temperature to tint the screen
         */
        void onColorTemperatureChanged(int colorTemperature);

    }
}
